package mirabilis.org;

import java.util.ArrayList;
import java.util.ListIterator;

import random.CommonState;

import mirabilis.org.config.Configuration;
import mirabilis.org.simplices.Coordinates;
import mirabilis.org.simplices.Vertex;
import mirabilis.samples.cec2014.Cec2014;



public abstract class TwoDimensionGradientSimplex extends Thread{
	
	static Cec2014 bench = new Cec2014();
	
	public static void main(String[] args) {
		Configuration.F=2;
		Configuration.N=2;
		Configuration.upper_bound= 100;
		Configuration.lower_bound= -100;	
		
		
		
		double scaleratio = (Configuration.upper_bound - Configuration.lower_bound);
		double radius = Math.sqrt(((Configuration.upper_bound - Configuration.lower_bound)*(Configuration.upper_bound - Configuration.lower_bound)/2.0));
		
		double step = 50;//radius;
		double cum = 0;
		
		
		double p1x = 100;//CommonState.r.nextDouble()*scaleratio+Configuration.lower_bound;
		double p1y = -100;//CommonState.r.nextDouble()*scaleratio+Configuration.lower_bound;
		
		double p2x = 50;//CommonState.r.nextDouble()*scaleratio+Configuration.lower_bound;
		double p2y = -100;//CommonState.r.nextDouble()*scaleratio+Configuration.lower_bound;

		double p3x = 100;//CommonState.r.nextDouble()*scaleratio+Configuration.lower_bound;
		double p3y = -50;//CommonState.r.nextDouble()*scaleratio+Configuration.lower_bound;

				
		double [] grad = compute_gradient(p1x, p1y, p2x, p2y, p3x, p3y);
		
		double [] best = best_coord(p1x, p1y, p2x, p2y, p3x, p3y);
		
		
		
		double A = area(best[0], best[1], best[2], best[3], best[4], best[5]);
		
		System.out.println(best[0]+" "+best[1]+" "+(best[0]-(step*grad[0]))+" "+(best[1]-(step*grad[1]))+" "+best[2]+" "+best[3]+" "+best[4]+" "+best[5]);
		
		
		for(int i=0;i<100;i++){
			
			double planetx,planety;
			
			double newx = best[0]-(step*grad[0]);
			double newy = best[1]-(step*grad[1]);
			
			if(newx < Configuration.lower_bound){
				planetx = Configuration.upper_bound - Math.abs((newx)%Configuration.lower_bound);
			}else if (newx > Configuration.upper_bound){
				planetx = Configuration.lower_bound + (newx)%Configuration.upper_bound;
			}else
				planetx = newx;
			
			if(newy < Configuration.lower_bound){
				planety = Configuration.upper_bound - Math.abs((newy)%Configuration.lower_bound);
			}else if (newy > Configuration.upper_bound){
				planety = Configuration.lower_bound + (newy)%Configuration.upper_bound;
			}else
				planety = newy;
			
			
			best[4] = planetx;
			best[5] = planety;
			
			grad = compute_gradient(best[0], best[1], best[2], best[3], best[4], best[5]);
			best = best_coord(best[0], best[1], best[2], best[3], best[4], best[5]);
			
			System.out.println(best[0]+" "+best[1]+" "+(best[0]-(step*grad[0]))+" "+(best[1]-(step*grad[1]))+" "+best[2]+" "+best[3]+" "+best[4]+" "+best[5]);
			
			step = step*0.84;

			
		}
		
	}
	
	public static double area(double p1x, double p1y, double p2x, double p2y, double p3x, double p3y){
		
		double a = Math.sqrt(Math.pow(p1x-p2x,2)+Math.pow(p1y-p2y,2)); 
		double b = Math.sqrt(Math.pow(p1x-p3x,2)+Math.pow(p1y-p3y,2));
		double c = Math.sqrt(Math.pow(p3x-p2x,2)+Math.pow(p3y-p2y,2));
		
		double s = (a+b+c)/2.0;
		double area = Math.sqrt(s*(s-a)*(s-b)*(s-c));
		
		return area;
	}
	
	public static double[] compute_gradient(double p1x, double p1y, double p2x, double p2y, double p3x, double p3y){
		double[] genes = {p1x,p1y};
		double fp1 = bench.evaluate(genes); 
		
		double[] genes2 = {p2x,p2y};
		double fp2 = bench.evaluate(genes2); 
		
		double[] genes3 = {p3x,p3y};
		double fp3 = bench.evaluate(genes3); 

		double [] p1 = {p1x,p1y,fp1};
		double [] p2 = {p2x,p2y,fp2};
		double [] p3 = {p3x,p3y,fp3};
		
		double [] grad = unit_gradient(p1, p2, p3);
		return grad;
	}
	
	public static double[] best_coord(double p1x, double p1y, double p2x, double p2y, double p3x, double p3y){
		
		double[] genes = {p1x,p1y};
		double fp1 = bench.evaluate(genes); 
		
		double[] genes2 = {p2x,p2y};
		double fp2 = bench.evaluate(genes2); 
		
		double[] genes3 = {p3x,p3y};
		double fp3 = bench.evaluate(genes3); 

		double [] p1 = {p1x,p1y,fp1};
		double [] p2 = {p2x,p2y,fp2};
		double [] p3 = {p3x,p3y,fp3};
		

		double [] rank = new double[2*3];
		
		if (fp1<=fp2 && fp1<=fp3){
			if(fp2 <= fp3){
				System.arraycopy(p1, 0, rank, 0, 2);
				System.arraycopy(p2, 0, rank, 2, 2);
				System.arraycopy(p3, 0, rank, 4, 2);
			}else{
				System.arraycopy(p1, 0, rank, 0, 2);
				System.arraycopy(p3, 0, rank, 2, 2);
				System.arraycopy(p2, 0, rank, 4, 2);
			}
		}else if (fp2<=fp1 && fp2<=fp3){
			if(fp1 <= fp3){
				System.arraycopy(p2, 0, rank, 0, 2);
				System.arraycopy(p1, 0, rank, 2, 2);
				System.arraycopy(p3, 0, rank, 4, 2);
			}else{
				System.arraycopy(p2, 0, rank, 0, 2);
				System.arraycopy(p3, 0, rank, 2, 2);
				System.arraycopy(p1, 0, rank, 4, 2);
			}
		}else{
			if(fp1 <= fp2){
				System.arraycopy(p3, 0, rank, 0, 2);
				System.arraycopy(p1, 0, rank, 2, 2);
				System.arraycopy(p2, 0, rank, 4, 2);
			}else{
				System.arraycopy(p3, 0, rank, 0, 2);
				System.arraycopy(p2, 0, rank, 2, 2);
				System.arraycopy(p1, 0, rank, 4, 2);
			}
		}
		
		return rank;
	}
	
public static double[] worst_coord(double p1x, double p1y, double p2x, double p2y, double p3x, double p3y){
		
		double[] genes = {p1x,p1y};
		double fp1 = bench.evaluate(genes); 
		
		double[] genes2 = {p2x,p2y};
		double fp2 = bench.evaluate(genes2); 
		
		double[] genes3 = {p3x,p3y};
		double fp3 = bench.evaluate(genes3); 

		double [] p1 = {p1x,p1y,fp1};
		double [] p2 = {p2x,p2y,fp2};
		double [] p3 = {p3x,p3y,fp3};
		
		double [] grad = unit_gradient(p1, p2, p3);
		
		if (fp1>fp2 && fp1>fp3)
			return p1;
		if (fp2>fp1 && fp2>fp3)
			return p2;
		else
			return p3;
	}
	
	public static double[] unit_gradient(double[] p1,double[] p2,double[] p3){
		
		double[] grad = gradient(p1, p2, p3);
		
		double a = Math.pow(grad[0], 2);
		double b = Math.pow(grad[1], 2);
		
		double absolute =	Math.sqrt(a+b);
		double[] unit = new double[grad.length];
		
		unit[0] = grad[0] / absolute;
		unit[1] = grad[1] / absolute;
		
		return unit;
	}
	
	public static double[] gradient(double[] p1,double[] p2,double[] p3){
		
		double [] v1 = new double[p1.length];
		double [] v2 = new double[p1.length];
		
		//cross product
		double [] u = new double[p1.length]; 
		
		for(int i=0;i<v1.length;i++){
			v1[i] = p2[i] - p1[i];
			v2[i] = p3[i] - p1[i];
		}
		
		//v1 x v2
		u[0] = v1[1] * v2[2] - v1[2] * v2[1]; // a
		u[1] = v1[2] * v2[0] - v1[0] * v2[2]; // b
		u[2] = v1[0] * v2[1] - v1[1] * v2[0]; // c
		
		//ecuacion del plano hasta aqui aX + bY + c f(x,y) = 0
		
		//para el gradiente f(x,y) = -a/c X + -b/c Y  
		
		double [] grad = new double[2];
		
		grad[0] = - (u[0] / u[2]);
		grad[1] = - (u[1] / u[2]);
		
		//System.out.println("AA"+grad[0]+" "+grad[1]);
			
		return grad;
	}
}
